#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <cstring>

const std::string pathname = "/home/cp";
const int proj_id = 0x666;
#define User 1
#define Creater 2
#define SIZE 4096

class Shm
{
private:
    //  公共的key
    key_t GetCommKey()
    {
        key_t key = ftok(pathname.c_str(), proj_id);
        if (key < 0)
            perror("ftok");
        return key;
    }

    // 获取共享内存
    int GetShmHeaper(key_t key, int flag)
    {
        int shmi = shmget(key, SIZE, flag);
        if (shmi < 0)
            perror("shmget");
        return shmi;
    }

    // 以创建者的身份获取共享内存
    bool GetShmForGrater()
    {
        if (_who == Creater)
        {
            _shmi = GetShmHeaper(_key, IPC_CREAT | IPC_EXCL | 0666); // 这里不带权限看不到挂接
            std::cout << _who << "Creater shm done..." << std::endl;

            if (_shmi > 0)
                return true;
        }
        return false;
    }

    // 以使用者的身份获取共享内存
    bool GetShmForUser()
    {
        if (_who == User)
        {
            _shmi = GetShmHeaper(_key, IPC_CREAT | 0666);
            std::cout << _who << "Get shm done..." << std::endl;
            if (_shmi > 0)
                return true;
        }
        return false;
    }

    // 获取当前的使用者是谁
    std::string GetCurrUsr(int who)
    {
        if (who == Creater)
            return "gCreater";
        else if (who == User)
            return "gUser";
        else
            return "None";
    }

    // 将共享内存挂载到各自的虚拟地址空间
    void *AttachShm()
    {
        void *addrshm = shmat(_shmi, nullptr, 0);
        if (addrshm == nullptr)
        {
            perror("shmat");
        }
        std::cout << "who: " << GetCurrUsr(_who) << "  attach shm...." << std::endl;
        return addrshm;
    }

    // 取消挂载
    int DetachShm(void *addrshm)
    {
        int res = shmdt(addrshm);
        if (res < 0)
        {
            perror("shmdt");
        }
        std::cout << "who: " << GetCurrUsr(_who) << "  detach shm...." << std::endl;
        return res;
    }

    // 获取共享内存的起始地址
    void *Addrshm()
    {
        return _addrshm;
    }

public:
    Shm(int who)
        : _who(who), _addrshm(nullptr)
    {
        _key = GetCommKey();
        if (_who == Creater)
            GetShmForGrater();
        else
            GetShmForUser();

        _addrshm = AttachShm();//挂接

        printf("key: 0x%x\n", _key);
        printf("shmi: %d\n", _shmi);
    }

    ~Shm()
    {
        if (_addrshm != nullptr) // 在析构时，取消挂接
            DetachShm(_addrshm);

        if (_who == Creater)
        {
            sleep(2);
            shmctl(_shmi, IPC_RMID, nullptr);
        }
        std::cout << "shm remove done..." << std::endl;
    }

    // 将共享内存清空
    void Clear()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, SIZE);
        }
    }

    // 发送消息
    void SendMessage(const std::string &msg)
    {
        while (true)
        {
            void *addrshm = Addrshm();
            memcpy(addrshm, msg.c_str(), msg.size() + 1);
            sleep(1);
        }
    }

    // 接收消息
    void RecerveMessage()
    {
        void *addrshm = Addrshm();
        while (true)
        {
            std::cout << "msg: " << (char *)addrshm << std::endl;
            sleep(1);
        }
    }

private:
    int _shmi;
    key_t _key;

    int _who;
    void *_addrshm;
};